package cn.nexd.location;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONStringer;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.nexd.location.collector.NexdCollectorAgent;
import cn.nexd.location.collector.NexdCollectorConfig;
import cn.nexd.location.collector.bean.BluetoothCollectorResult;
import cn.nexd.location.collector.bean.WifiCollectorResult;
import cn.nexd.location.collector.core.Collector;
import cn.nexd.location.thread.HandlerLocationEventThread;
import cn.nexd.location.thread.NexdIndoorLocationThread;
import cn.nexd.location.utils.AndroidUtil;
import cn.nexd.location.utils.Command;
import cn.nexd.location.utils.smoothness.DefaultBluetoothSmoothnessStrategy;
import cn.nexd.location.utils.smoothness.DefaultWifiSmoothnessStrategy;
import cn.nexd.location.utils.smoothness.SmoothnessContext;

/**
 * Nexd 定位服务类。
 * Created by codingfish on 15/9/28.
 */
public class NexdLocationAgent {

    private final String TAG = this.getClass().getSimpleName();
    private Context context;
    private NexdLocationConfig locationConfig;
    private final NexdCollectorAgent collectorAgent;
    private NexdLocationListener nexdLocationListener;
    private final NexdCollectorConfig.Buidler collectorConfig;

    private NexdIndoorLocationThread nexdIndoorLocationThread;
    private long buildingId;
    private long floorId = -1L;
    private boolean locationEngineRunning = false;
    private HandlerLocationEventThread handlerLocationEventThread;

    private boolean isInputSmooth = false;

    private int collectorTypeWifi = 0;
    private final SmoothnessContext smoothnessContext;
    private Collector.WifiCollectorListener wifiCollectorListener;
    private Collector.BluetoothCollectorListener bluetoothCollectorListener;

    private NexdLocationAgent() {
        collectorAgent = NexdCollectorAgent.getCollectorAgent();
        collectorConfig = new NexdCollectorConfig.Buidler();
        smoothnessContext = new SmoothnessContext(new DefaultWifiSmoothnessStrategy(), new DefaultBluetoothSmoothnessStrategy());
    }


    /**
     * 返回 NexdLocationAgent 对象
     *
     * @return NexdLocationAgent
     */
    public static final NexdLocationAgent getLocationAgent() {
        return NexdLocationAgentClassHolder.NEXD_LOCATION_AGENT;
    }

    public void setLocationListener(NexdLocationListener nexdLocationListener) {
        this.nexdLocationListener = nexdLocationListener;
    }

    /**
     * 设置上下文对象。
     *
     * @param context 当前上下文对象
     */
    public void setContext(Context context) {
        this.context = context;
        collectorAgent.setContext(context);
    }


    /**
     * 设置 Wifi 定位数据回传接口
     *
     * @param wifiCollectorListener
     */
    public void setWifiCollectorListener(Collector.WifiCollectorListener wifiCollectorListener) {
        this.wifiCollectorListener = wifiCollectorListener;
    }

    /**
     * 设置蓝牙定位数据回传接口
     *
     * @param bluetoothCollectorListener
     */
    public void setBluetoothCollectorListener(Collector.BluetoothCollectorListener bluetoothCollectorListener) {
        this.bluetoothCollectorListener = bluetoothCollectorListener;
    }

    /**
     * 设置 Location 的配置对象。
     *
     * @param locationConfig
     * @see NexdLocationConfig
     */
    public void setLocationConfig(NexdLocationConfig locationConfig) {
        this.locationConfig = locationConfig;
        this.collectorConfig.setAppkey(locationConfig.appKey);
        this.collectorConfig.setWifiCollectorRate(locationConfig.locationRate);
        this.collectorConfig.setBluetoothCollectorRate(locationConfig.locationRate);
        switch (locationConfig.locationMode) {
            case WIFI_ONLY:
                collectorConfig.setWifiCollectorEnable(true);
                collectorConfig.setBluetoothCollectorEnable(false);
                break;
            case BLUETOOTH_ONLY:
                collectorConfig.setWifiCollectorEnable(false);
                collectorConfig.setBluetoothCollectorEnable(true);
                break;
            case MIX:
                collectorConfig.setWifiCollectorEnable(true);
                collectorConfig.setBluetoothCollectorEnable(true);
                break;
        }
    }

    public void startLocation() {

        if (locationEngineRunning) {
            return;
        }
        locationEngineRunning = true;
        //在楼 ID 楼层 ID 未知额情况下定位
        checkLocationConfig();

    }

    public void startLocation(long buildingId) {
        this.buildingId = buildingId;
        if (locationEngineRunning) {
            return;
        }
        locationEngineRunning = true;
        //在楼层 ID 未知的情况下定位
        checkLocationConfig();

        startCollector(buildingId, -1L);
    }


    public void startLocation(long buildingId, long floorId) {
        this.buildingId = buildingId;
        this.floorId = floorId;
        if (locationEngineRunning) {
            return;
        }
        locationEngineRunning = true;
        checkLocationConfig();

        startCollector(buildingId, floorId);
    }

    public void startCollector() {
        checkLocationConfig();
        collectorAgent.setWifiCollectorListener(new DefauleWifiCollectorListener());
        collectorAgent.setBluetoothCollectorListener(new DefaultBluetoothCollectorListener());
        collectorAgent.setContext(context);
        collectorAgent.startCollector();
    }

    public void stopCollector() {
        collectorAgent.stopCollector();
    }

    public void destroyCollector() {
        collectorAgent.destroyCollector();
    }

    private final void startCollector(long buildingId, long floorId) {
        if (nexdIndoorLocationThread == null) {
            nexdIndoorLocationThread = new NexdIndoorLocationThread(nexdLocationListener);
        }

        nexdIndoorLocationThread.iniConfig(buildingId, floorId);
        collectorAgent.setWifiCollectorListener(new DefauleWifiCollectorListener());
        collectorAgent.setBluetoothCollectorListener(new DefaultBluetoothCollectorListener());
        collectorAgent.setContext(context);
        collectorAgent.startCollector();
    }


    public boolean getLocationEngineState() {
        return locationEngineRunning;
    }

    private void checkLocationConfig() {
        if (locationConfig == null) {
            NexdLocationConfig.Buidler buidler = new NexdLocationConfig.Buidler();
            buidler.setAppkey("");
            buidler.setLocationMode(NexdLocationConfig.LocationMode.WIFI_ONLY);
            this.setLocationConfig(buidler.build());
            locationConfig = buidler.build();
        }
        collectorAgent.setCollectorConfig(collectorConfig.build());

        //初始化存放缓存文件的路径
        if (TextUtils.isEmpty(this.locationConfig.locationConfigPath)) {
            Command.setNexdBaseFilePath(AndroidUtil.getDiskCacheDir(context, "nexd"));
        } else {
            Command.setNexdBaseFilePath(locationConfig.locationConfigPath);
        }
        //初始化处理失败和中断的业务
        handlerLocationEventThread = new HandlerLocationEventThread(nexdLocationListener);
    }

    public void stopLocation() {
        if (collectorAgent != null) {
            collectorAgent.stopCollector();
            collectorAgent.setContext(null);
            collectorAgent.setWifiCollectorListener(null);
        }

        if (nexdIndoorLocationThread != null) {
            nexdIndoorLocationThread.stopThread();
        }

        if (handlerLocationEventThread != null) {
            handlerLocationEventThread.stop();
        }

        locationEngineRunning = false;
    }

    public void destroy() {
        if (context != null) {
            context = null;
        }

        if (collectorAgent != null) {
            collectorAgent.destroyCollector();
        }
        handlerLocationEventThread = null;
        nexdIndoorLocationThread = null;
    }

    private final void locationEngine(final long collectorResultTaskId, long collectorResultTaskTime, List<WifiCollectorResult> wifiCollectorResults, List<BluetoothCollectorResult> bluetoothCollectorResults) {
        if (nexdIndoorLocationThread == null) {
            nexdIndoorLocationThread = new NexdIndoorLocationThread(nexdLocationListener);
        }
        nexdIndoorLocationThread.stopThread();

        //完善混合定位
    }

    private final void locationEngineWifi(final long collectorResultTaskId, long collectorResultTaskTime, List<WifiCollectorResult> wifiCollectorResults) {
        if (nexdIndoorLocationThread == null) {
            nexdIndoorLocationThread = new NexdIndoorLocationThread(nexdLocationListener);
        }
        nexdIndoorLocationThread.stopThread();
        nexdIndoorLocationThread.start(collectorResultTaskId, buildingId, floorId, generateWifiJsonObject(wifiCollectorResults));
    }

    private final void locationEngineBluetooth(final long collectorResultTaskId, long collectorResultTaskTime, List<BluetoothCollectorResult> bluetoothCollectorResults) {
        if (nexdIndoorLocationThread == null) {
            nexdIndoorLocationThread = new NexdIndoorLocationThread(nexdLocationListener);
        }
        nexdIndoorLocationThread.stopThread();
        nexdIndoorLocationThread.start(collectorResultTaskId, buildingId, floorId, generateBluetoothJsonObject(bluetoothCollectorResults));
    }

    //    private final void locationEngine(final long collectorResultTaskId, long collectorResultTaskTime, String wifiCollectorResults) {
    //        if (nexdIndoorLocationThread == null) {
    //            nexdIndoorLocationThread = new NexdIndoorLocationThread(nexdLocationListener);
    //        }
    //        nexdIndoorLocationThread.stopThread();
    //        nexdIndoorLocationThread.start(buildingId, floorId, wifiCollectorResults);
    //    }


    private static synchronized final String generateWifiJsonObject(List<WifiCollectorResult> wifiCollectorResults) {
        List<WifiCollectorResult> wifiCollectorResultList = wifiCollectorResults;
        JSONStringer jsonStringer = new JSONStringer();
        try {
            jsonStringer.object();
            for (WifiCollectorResult wifiCollectorResult : wifiCollectorResultList) {
                jsonStringer.key(wifiCollectorResult.getBSSID()).value(String.valueOf(wifiCollectorResult.getLevel()));
            }
            //            wifiCollectorResults.clear();
            jsonStringer.endObject();
            return jsonStringer.toString();
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static synchronized final String generateBluetoothJsonObject(List<BluetoothCollectorResult> bluetoothCollectorResults) {
        List<BluetoothCollectorResult> bluetoothCollectorResultList = bluetoothCollectorResults;
        JSONStringer jsonStringer = new JSONStringer();
        try {
            jsonStringer.object();
            for (BluetoothCollectorResult bluetoothCollectorResult : bluetoothCollectorResultList) {
                jsonStringer.key(bluetoothCollectorResult.getAddress()).value(String.valueOf(bluetoothCollectorResult.getRssi()));
            }
            //            bluetoothCollectorResults.clear();
            jsonStringer.endObject();
            return jsonStringer.toString();
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static class NexdLocationAgentClassHolder {
        public static final NexdLocationAgent NEXD_LOCATION_AGENT = new NexdLocationAgent();
    }

    private class DefauleWifiCollectorListener implements Collector.WifiCollectorListener {

        @Override
        public void onFailed(long collectorResultTaskId, long collectorResultTaskTime, int collectorResultTaskFailedType, String collectorResultTaskFailedMessage) {
            Log.d("collector_test_data", "" + collectorResultTaskFailedMessage);
            switch (locationConfig.locationMode) {
                case MIX:
                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onFailed(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskFailedType, collectorResultTaskFailedMessage);
                    }
                    break;
                case WIFI_ONLY:
                    if (handlerLocationEventThread != null && nexdLocationListener != null) {
                        handlerLocationEventThread.handler(collectorResultTaskFailedType, collectorResultTaskFailedMessage, collectorTypeWifi);
                    }

                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onFailed(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskFailedType, collectorResultTaskFailedMessage);
                    }
                    break;
                case BLUETOOTH_ONLY:
                    break;
            }
        }

        @Override
        public void onStop(long collectorResultTaskId, long collectorResultTaskTime, int collectorResultTaskStopType, String collectorResultTaskStopMessage) {
            Log.d("collector_test_data", "" + collectorResultTaskStopMessage);
            locationEngineRunning = false;
            switch (locationConfig.locationMode) {
                case MIX:
                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onStop(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskStopType, collectorResultTaskStopMessage);
                    }
                    break;
                case WIFI_ONLY:
                    if (handlerLocationEventThread != null && nexdLocationListener != null) {
                        handlerLocationEventThread.handler(collectorResultTaskStopType, collectorResultTaskStopMessage, collectorTypeWifi);
                    }

                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onStop(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskStopType, collectorResultTaskStopMessage);
                    }
                    break;
                case BLUETOOTH_ONLY:
                    break;
            }
        }

        @Override
        public void onSuccess(long collectorResultTaskId, long collectorResultTaskTime, List<WifiCollectorResult> wifiCollectorResults) {


            //            if (nexdLocationListener != null) {
            switch (locationConfig.locationMode) {
                case MIX:
                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onSuccess(collectorResultTaskId, collectorResultTaskTime, wifiCollectorResults);
                    }
                    break;
                case WIFI_ONLY:
                    if (wifiCollectorListener != null) {
                        wifiCollectorListener.onSuccess(collectorResultTaskId, collectorResultTaskTime, wifiCollectorResults);
                        Log.d("collector_test_data", "locationAgent 收到的 wifi 数量 " + wifiCollectorResults.size());
                    }
                    if (nexdLocationListener != null) {
                        if (locationConfig.smoothness) {
                            locationEngineWifi(collectorResultTaskId, collectorResultTaskTime, smoothnessContext.executeSmoothnessWifi(wifiCollectorResults));
                        } else {
                            locationEngineWifi(collectorResultTaskId, collectorResultTaskTime, wifiCollectorResults);
                        }
                    }
                    break;
                case BLUETOOTH_ONLY:
                    break;
            }
            //            }


        }
    }

    private class DefaultBluetoothCollectorListener implements Collector.BluetoothCollectorListener {
        @Override
        public void onFailed(long collectorResultTaskId, long collectorResultTaskTime, int collectorResultTaskFailedType, String collectorResultTaskFailedMessage) {
            Log.d("bluetooth", "" + collectorResultTaskFailedMessage);
            switch (locationConfig.locationMode) {
                case MIX:
                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onFailed(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskFailedType, collectorResultTaskFailedMessage);
                    }
                    break;
                case BLUETOOTH_ONLY:
                    if (handlerLocationEventThread != null && nexdLocationListener != null) {
                        handlerLocationEventThread.handler(collectorResultTaskFailedType, collectorResultTaskFailedMessage, collectorTypeWifi);
                    }
                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onFailed(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskFailedType, collectorResultTaskFailedMessage);
                    }
                    break;
                case WIFI_ONLY:
                    break;
            }
        }

        @Override
        public void onStop(long collectorResultTaskId, long collectorResultTaskTime, int collectorResultTaskStopType, String collectorResultTaskStopMessage) {
            Log.d("bluetooth", "" + collectorResultTaskStopMessage);
            locationEngineRunning = false;
            switch (locationConfig.locationMode) {
                case MIX:
                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onStop(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskStopType, collectorResultTaskStopMessage);
                    }
                    break;
                case BLUETOOTH_ONLY:
                    if (handlerLocationEventThread != null && nexdLocationListener != null) {
                        handlerLocationEventThread.handler(collectorResultTaskStopType, collectorResultTaskStopMessage, collectorTypeWifi);
                    }

                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onStop(collectorResultTaskId, collectorResultTaskTime, collectorResultTaskStopType, collectorResultTaskStopMessage);
                    }
                    break;
                case WIFI_ONLY:
                    break;
            }
        }

        private Map<String, BluetoothCollectorResult> blueMap = new HashMap<String, BluetoothCollectorResult>();

        private List<BluetoothCollectorResult> reCala(List<BluetoothCollectorResult> bluetoothCollectorResults) {
            for (BluetoothCollectorResult bluetoothCollectorResult : bluetoothCollectorResults) {
                if (blueMap.containsKey(bluetoothCollectorResult.getAddress())) {
                    blueMap.get(bluetoothCollectorResult.getAddress())
                            .setRssi(
                                    avg(
                                            blueMap.get(bluetoothCollectorResult.getAddress()).getRssi(),
                                            bluetoothCollectorResult.getRssi()));
                } else {
                    blueMap.put(bluetoothCollectorResult.getAddress(), bluetoothCollectorResult);
                }
            }

            bluetoothCollectorResults.clear();

            for (Map.Entry<String, BluetoothCollectorResult> map : blueMap.entrySet()) {
                bluetoothCollectorResults.add(map.getValue());
            }

            Log.d(TAG, "reCala: " + Arrays.toString(bluetoothCollectorResults.toArray()));
            blueMap.clear();
            return bluetoothCollectorResults;
        }

        private int avg(int... arg) {
            int result = 0;
            for (int i : arg) {
                result += i;
            }
            return result / arg.length;
        }

        @Override
        public void onSuccess(long collectorResultTaskId, long collectorResultTaskTime, List<BluetoothCollectorResult> bluetoothCollectorResults) {
            Log.d("bluetooth", "" + "#####################  " + bluetoothCollectorResults.size());

            switch (locationConfig.locationMode) {
                case MIX:
                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onSuccess(collectorResultTaskId, collectorResultTaskTime, reCala(bluetoothCollectorResults));
                    }
                    break;
                case BLUETOOTH_ONLY:

                    if (bluetoothCollectorListener != null) {
                        bluetoothCollectorListener.onSuccess(collectorResultTaskId, collectorResultTaskTime, reCala(bluetoothCollectorResults));
                    }

                    if (nexdLocationListener != null) {
                        if (locationConfig.smoothness) {
                            locationEngineBluetooth(collectorResultTaskId, collectorResultTaskTime, smoothnessContext.executeSmoothnessBluetooth(reCala(bluetoothCollectorResults)));
                        } else {
                            locationEngineBluetooth(collectorResultTaskId, collectorResultTaskTime, reCala(bluetoothCollectorResults));
                        }
                    }


                    break;
                case WIFI_ONLY:
                    break;
            }
        }
    }
}
